home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-05
/
pcrte224.zip
/
SOURCE.ZIP
/
WD8003.INC
< prev
next >
Wrap
Text File
|
1992-06-09
|
20KB
|
510 lines
;;******************************************************************************
;; wd8003.inc wd8003.inc
;;******************************************************************************
;;
;; Copyright (C) 1989 Northwestern University, Vance Morrison
;;
;;
;; Permission to view, compile, and modify for LOCAL (intra-organization)
;; USE ONLY is hereby granted, provided that this copyright and permission
;; notice appear on all copies. Any other use by permission only.
;;
;; Northwestern University makes no representations about the suitability
;; of this software for any purpose. It is provided "as is" without expressed
;; or implied warranty. See the copywrite notice file for complete details.
;;
;;******************************************************************************
;; wd8003 holds the interface routines for the western digital ethernet card
;; WD8003E or the starlan card WD8003S.
;;
;; The functions provided by this file are
;;
;; WD_DECLARE name, io_address, shr_seg, shr_off
;; WD_DEFINE name
;; WD_IF_R_ACCESS_out_BX_CX_ES name, no_packet
;; WD_IF_R_CONT_in_BX_CX_ES_const_BX_CX_DX_BP_SI_DI_ES name, ok
;; WD_IF_R_FREE_const_BX_CX_BP_SI_DI_ES name
;; WD_IF_W_ACCESS_in_CX_out_DI_ES_const_BX_CX_BP name, no_buffer
;; WD_IF_W_WRITE_in_CX_const_BX_BP_ES name
;; WD_IF_SET_ADDRESS_in_SI_const_BX_CX_BP_DI_ES name
;; WD_IF_COPY_in_CX_SI_DI_ES_out_SI_DI_const_BX_BP_ES name
;;
;; Variables set by this module
;;
;; wd_&name&_declared ;; one if this interface exists
;; if_&name&_address ;; the hardware address
;; if_&name&_mtu ;; the max trans unit
;;
;;******************************************************************************
include wd.inc
;;******************************************************************************
;; data storage needed by this module
wd_q_entry STRUC
wd_q_start DW ?
wd_q_end DW ?
wd_q_len DW ?
wd_q_entry ENDS
wd_data STRUC
wd_new_bndry DB 0
wd_dequeue DB 0 ;; please dequeue the last packet
wd_data ENDS
;;******************************************************************************
;; IF_DECLARE name, io_address, shr_seg, shr_off, wbuffs
;; declares an interface object. 'io_address' is the address of the
;; start of the 8003E control registers. 'shr_seg' and
;; 'shr_off' is the address of the WD8003 card buffer
;; This address must be a multiple of 512. 'timer' is a timer
;; object. 'wbuffs' is the number of 256byte blocks to allocate to
;; writing (should be 6 for ethernet, more like 16 for starlan)
;;
WD_DECLARE MACRO name, io_address, shr_seg, shr_off, timer, wbuffs
.errb <name>
.errb <io_address>
.errb <shr_seg>
.errb <shr_off>
.DATA
wd_&name&_declared = 1
wd_&name&_io = io_address ;; set compile time values
wd_&name&_shared_off = shr_off
wd_&name&_shared_seg = shr_seg
wd_&name&_timer = timer
wd_&name&_buff = name*100+1 ;; make up names in my space
wd_&name&_wqueue = name*100+2
ifb <wbuffs>
wd_&name&_wbuffs = 6
else
wd_&name&_wbuffs = wbuffs
endif
if_&name&_mtu = 1514
global wd_&name&_data:wd_data
global if_&name&_address:word
.CODE
global wd_&name&_dequeue:near
global wd_&name&_real_define:near
BUFF_DECLARE %wd_&name&_buff, %(wbuffs*256), shr_off
QUEUE_DECLARE %wd_&name&_wqueue, wbuffs, %(size wd_q_entry)
ENDM
;;******************************************************************************
;; IF_DEFINE name
;; sets asside memory an interface object and initializes it. This
;; routine is a no-op if 'name' was not declared
;;
WD_DEFINE MACRO name
call wd_&name&_real_define
ENDM
WD_REAL_DEFINE MACRO name
LOCAL loop1, loop2, loop3, around
.errb <name>
ifdef wd_&name&_declared
.DATA
if_&name&_address DW 3 DUP (0)
wd_&name&_data wd_data <> ;; create storage needed
.CODE
jmp around
wd_&name&_dequeue:
WD_IF_W_DEQUEUE_const_BX_BP_ES name
TIMER_RETURN %wd_&name&_timer
around:
wd_&name&_real_define:
BUFF_DEFINE %wd_&name&_buff ;; initialize the buff manager
QUEUE_DEFINE %wd_&name&_wqueue ;; and write queue
mov cx, 6 ;; get the ethernet address
mov bx, OFFSET if_&name&_address
mov dx, wd_&name&_io+ADDROM ;; point to the begining of the io space
loop1: ;; which is the reg holding the Eth addr
in AL, DX
mov [BX], AL
inc DX
inc BX
loop loop1
mov AL, 80h ;; reset the card
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES 0, wd_&name&_io
mov AL, 00h
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES 0, wd_&name&_io
;; register 0 is the MSR (Memory base reg)
mov AL, (wd_&name&_shared_seg+(wd_&name&_shared_off/16))/512
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES 0, wd_&name&_io
mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are on page 0
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
mov AL, MSK_BMS + MSK_FT10 ;; select FIFO threshold = 8 bytes
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES DCR, wd_&name&_io
xor AL, AL ;; clear RBCR0,1
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RBCR0, wd_&name&_io
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RBCR1, wd_&name&_io
mov AL, MSK_MON ;; turn off receiving
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RCVR, wd_&name&_io
xor AL, AL ;; clear TCR
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TCR, wd_&name&_io
mov AL, STOP_PG ;; end of input buffer (in 256b pages)
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES PSTOP, wd_&name&_io
mov AL, wd_&name&_wbuffs ;; start of input buffer (in 256b pages)
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES PSTART, wd_&name&_io
;; Boundry of read in from not read in yet
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io
mov AL, -1
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES ISR, wd_&name&_io
mov AL, 0 ;; no interupts
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES IMR, wd_&name&_io
mov AL, MSK_PG1 + MSK_RD2 ;; make sure we are on page 1
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
mov AL, wd_&name&_wbuffs+1 ;; Set input pointer for queue
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CURR, wd_&name&_io
mov CX, 6 ;; set the ethernet address
mov BX, OFFSET if_&name&_address
mov DX, wd_&name&_io+PAR0
loop2:
mov AL, [BX] ;; get 1 byte into AL
out DX, AL ;; write to PAR
inc BX
inc DX
loop loop2
mov CX, 8 ;; set the multicast address to all 0's
mov DX, wd_&name&_io+MAR0
xor AL, AL
loop3:
out DX, AL
inc DX
loop loop3
mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are on page 0
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
mov AL, MSK_STA + MSK_RD2 ;; activate the 8390
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
mov AL, MSK_AB ;; turn on receiver
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RCVR, wd_&name&_io
mov byte ptr wd_&name&_data.wd_dequeue, 0 ;; no packet to dequeue
RET
endif
ENDM
;;******************************************************************************
;; IF_R_ACCESS_out_BX_ES name, no_packet
;; IF_R_ACCESS waits for the next packet to come from the the board
;; associated with 'name' and returns a pointer to the begining of
;; an ethernet packet in BX:ES. CX holds the length of the packet
;; R_ACCESS jumps to 'no_packet' if there are no packets waiting to
;; be read in
;;
WD_IF_R_ACCESS_out_BX_CX_ES MACRO name, no_packet
local inside, good_packet, wrapped, new_wrapped, bad_packet, ok_status
local good_length, truncate
.errb <no_packet>
mov AL, MSK_PG0 + MSK_RD2 ;; read the BNRY register into AL
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io
inc AL ;; increment with wrap around
cmp AL, STOP_PG
jb inside
mov AL, wd_&name&_wbuffs
inside:
mov BH, AL ;; save it in BH
mov AL, MSK_PG1 + MSK_RD2 ;; read CURR register into AL
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES CURR, wd_&name&_io
cmp AL, BH
je no_packet
xor BL, BL ;; BX now holds pointer to packet
mov DX, wd_&name&_shared_seg ;; ES = segment address
mov ES, DX
mov AH, ES:[BX+wd_&name&_shared_off] ;; get the status
cmp AH, SMK_PRX ;; is it good
jz ok_status
cmp AH, SMK_PRX+SMK_PHY
jnz bad_packet
ok_status:
mov AH, ES:[BX+1+wd_&name&_shared_off] ;; pointer to the next packet
;; sanity check on next packet pointer AH
cmp BH, AL ;; is BNDRY+1 <= CURR?
ja wrapped
cmp AH, BH
jb bad_packet
cmp AH, AL
jbe good_packet
jmp bad_packet
wrapped:
cmp AH, BH
jb new_wrapped
cmp AH, STOP_PG
jnb bad_packet
jmp good_packet
new_wrapped:
cmp AH, wd_&name&_wbuffs
jb bad_packet
cmp AH, AL
jbe good_packet
bad_packet:
;; set BNDRY = BNDRY+1 and try again
mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are on page 0
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
mov AL, BH ;; write the new BNRY register
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io
jmp no_packet ;; return bad status
good_packet:
mov wd_&name&_data.wd_new_bndry, AH ;; save it for R_FREE
mov CX, ES:[BX+wd_&name&_shared_off+2] ;; load the length
add BX, wd_&name&_shared_off+4 ;; BX point to begining of the packet
sub CX, 4
cmp CX, 1514 ;; sanity check
jle good_length
cmp CH, CL
jne truncate
xor CH, CH ;; fix western digital bug
jmp good_length
truncate:
mov CX, 1514
good_length:
ENDM
;;******************************************************************************
;; IF_R_FREE_const_BX_CX_BP_SI_DI_ES name
;; After the client is through processing the packet returned by
;; IF_R_ACCESS, IF_R_FREE must be called to inform 'name' that the
;; memory that the packet was in can be reused for future packets.
;;
WD_IF_R_FREE_const_BX_CX_BP_SI_DI_ES MACRO name
local inside
.errb <name>
mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are on page 0
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
mov AL, wd_&name&_data.wd_new_bndry ;; Retreive NEW_BOUNDRY
dec AL
cmp AL, wd_&name&_wbuffs
jge inside
mov AL, STOP_PG-1
inside: ;; write the new BNRY register
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io
ENDM
;;******************************************************************************
;; WD_IF_R_CONT_in_BX_CX_ES name, ok
;; IF_R_CONT determines if the packet returned by R_READ in BX:ES
;; of length CX is continuous. If it is it jumps to 'ok' otherwise
;; it just returns
;;
WD_IF_R_CONT_in_BX_CX_ES_const_BX_CX_DX_BP_SI_DI_ES MACRO name, ok
.errb <ok>
mov AX, BX
add AX, CX
cmp AX, OFFSET wd_&name&_shared_off+STOP_PG*256
jb ok
ENDM
;;******************************************************************************
;; IF_W_ACCESS_in_CX_out_DI_ES name, no_buffer
;; IF_W_ACCESS returns a pointer to an output buffer for a packet. The
;; pointer is returned in DI:ES. If the ouptut buffer is busy, this
;; routine will jump to 'no_buffer'. The output buffer min(CX, 1536)
;; bytes long
;;
WD_IF_W_ACCESS_in_CX_out_DI_ES_const_BX_CX_BP MACRO name, no_buffer
.errb <no_buffer>
BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES %wd_&name&_buff, no_buffer
mov DI, SI
mov SI, offset wd_&name&_shared_seg
mov ES, SI
ENDM
;;******************************************************************************
;; IF_W_WRITE_in_CX name
;; IF_W_WRITE actually signals the ethernet board to write a packet to
;; the ethernet. The packet is assumed to be in the buffer returned by
;; IF_W_ACCESS. CX is the length of the packet to send.
;;
WD_IF_W_WRITE_in_CX_const_BX_BP_ES MACRO name
local done
.errb <name>
;; queue up the packet
mov DX, CX ;; save CX
add CX, 255 ;; round up to a multiple of 256
xor CL, CL
BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES %wd_&name&_buff, done
mov CX, DI ;; save buff_end
QUEUE_ENQUEUE_out_DI_const_BX_CX_DX_BP_SI_ES %wd_&name&_wqueue, done
xchg CX, DI ;; restore DI, save queue entry
BUFF_GET_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %wd_&name&_buff
xchg DI, CX ;; restore queue entry, save_end
mov [DI+wd_q_start], SI
mov [DI+wd_q_end], CX
mov [DI+wd_q_len], DX
WD_IF_W_DEQUEUE_const_BX_BP_ES name, 1
done:
ENDM
;;******************************************************************************
;; WD_IF_W_DEQUEUE tryes to empty the write queue. It checks if there is
;; something in the queue, and if there is, it tries to send it. If the
;; queue is not empty when it leaves, it submits itself to go off again so
;; that it can try again. 'write' is true (not_blank) if this is called
;; from W_WRITE
;;
WD_IF_W_DEQUEUE_const_BX_BP_ES MACRO name, write
local done, requeue, not_dequeueing, no_more
.errb <name>
mov CL, wd_&name&_data.wd_dequeue ;; save the old value
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
test AL, MSK_TXP
ifnb <write>
jnz done
else
jnz requeue
endif
cmp CL, 0 ;; dequeue the next packet ?
jz not_dequeueing
QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %wd_&name&_wqueue,no_more
mov DI, word ptr [SI+wd_q_end]
BUFF_FREE_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %wd_&name&_buff
QUEUE_DEQUEUE_in_SI_const_AX_BX_CX_DX_BP_DI_ES %wd_&name&_wqueue
not_dequeueing:
;; is there another to send?
QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %wd_&name&_wqueue, no_more
;; send the packet
mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are in register page 0
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
mov AX, [SI+wd_q_len]
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TBCR0, wd_&name&_io
mov AL, AH
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TBCR1, wd_&name&_io
mov AL, byte ptr [SI+wd_q_start+1] ;; where the packet starts
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TPSR, wd_&name&_io
mov AL, MSK_TXP + MSK_RD2 ;; send the packet
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
mov wd_&name&_data.wd_dequeue, 1
ifnb <write>
or CL, CL
jnz done
endif
requeue:
;; submit it to the timer queue
xor AX, AX
mov CX, BX ;; save BX
TIMER_MARK_in_AX_const_CX_BP_ES %wd_&name&_timer, wd_&name&_dequeue
mov BX, CX ;; restore BX
jmp done
no_more:
mov wd_&name&_data.wd_dequeue, 0
done:
ENDM
;;******************************************************************************
;; IF_SET_ADDRESS_in_SI name
;; IF_SET_ADDRESS_in_SI sets the hardware address to be the value
;; pointed to by SI. Note this function may be a no-op if the
;; hardware address cannot be set (ETHERNET for example)
;;
WD_IF_SET_ADDRESS_in_SI_const_BX_CX_BP_DI_ES MACRO name
.err ;; we don't support setting ethernet addresses (yet)
ENDM
;;******************************************************************************
;; IF_COPY_in_CX_SI_DI_ES_out_SI_DI name
;; IF_COPY_in_CX_SI_DI_ES copys a packet from the input buffer (pointed
;; to by SI and the segement register given in IF_DECLARE) to an output
;; buffer (pointed to by DI and dest_reg) of length CX. It assumes the
;; output buffer is contiguous. (and the caller shouldn't care if the
;; input buffer is contiguous) COPY updates the pointers SI and DI
;; to the end of the packet, and COPY could be called again if CX is not
;; the total packet length (Note that CX MUST be even if you care about
;; SI, and DI being updated properly)
;;
WD_IF_COPY_in_CX_SI_DI_ES_out_SI_DI_const_BX_BP_ES MACRO name
local wrap, done
.errb <name>
mov DX, DS ;; save DS
mov AX, wd_&name&_shared_seg
mov DS, AX
mov AX, OFFSET wd_&name&_shared_off+STOP_PG*256
sub AX, SI ;; AX holds length to wrap line
CMP AX, CX
jl wrap ;; wrap if AX less than packet lenght
inc CX ;; AX >= CX, no wrap is necessary
shr CX,1
rep movsw
jmp done
wrap:
XCHG AX, CX ;; length is now length to wrap line
SUB AX, CX ;; AX holds remainder
inc CX
shr CX,1
rep movsw
mov SI, OFFSET wd_&name&_shared_off+wd_&name&_wbuffs*256
mov CX, AX
inc CX
shr CX,1
rep movsw
done:
mov DS, DX ;; restore DS
ENDM
;;******************************************************************************
;; utility functions needed only within this module
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES MACRO port, if_io
mov DX, if_io+port
in AL, DX ;; AL contains data read from port
ENDM
;;******************************************************************************
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES MACRO port, if_io
mov DX, if_io+port
out DX, AL ;; AL contains data read from port
ENDM